# -*- coding: utf-8 -*-
# created on 2016/10/24

from sympy import Rational, log, asin, acos, Symbol, S, Intersection, sympify, Number, fraction, Complement, FiniteSet
from itertools import chain
from mathsolver.functions.base.base import BaseFunction, new_latex
from mathsolver.functions.base.objects import BasePieceFunc, BaseInter
from mathsolver.functions.hanshu.helper import is_odd, solveset, check_func, check_inter
from sympy.abc import k


class QiuDingYiYu(BaseFunction):
    """求函数定义域"""

    def __init__(self, *args, **kwargs):
        super(QiuDingYiYu, self).__init__(*args, **kwargs)
        self.domain = S.Reals

    def solver(self, *args):
        """
        递归求函数定义域
        :param args: [0] BaseFunc - 表达式, 或者 BaseEq
        :return: Interval - 定义域
        """
        func = check_func(args[0])

        # 分段函数不需要考虑定义域
        if isinstance(func, BasePieceFunc):
            return S.Reals

        self.__recur_domain(func.expression, func.var)
        definition_field = self.domain

        # 补充：可能给定区间
        if len(args) > 1:
            interval = check_inter(args[1])
            definition_field = definition_field.intersect(interval)
            # 特殊处理：三角函数定义域含有 k
            if isinstance(definition_field, Complement) and definition_field.has(k):
                left_interval, finitesets = definition_field.args
                from mathsolver.functions.sanjiao.sanjiao_utils import expr_value_list
                fs_to_subtract = chain(*[expr_value_list(item, interval) for item in finitesets])
                definition_field = FiniteSet(*fs_to_subtract).complement(left_interval)

        return definition_field

    @staticmethod
    def __is_sqrt(e):
        return e.is_Pow and e.args[1] == Rational(1, 2) and len(e.args[0].free_symbols) > 0

    @staticmethod
    def __is_log(e):
        return isinstance(e, log) and len(e.args[0].free_symbols) > 0

    @staticmethod
    def __is_negpow(e):
        """(f(x))**(m/n) 形式"""
        return e.is_Pow and not e.args[1].free_symbols and e.args[1] < 0

    @staticmethod
    def __is_invrat(e):
        return e.is_Pow and e.args[1] == -1 and len(e.args[0].free_symbols) > 0

    @staticmethod
    def __is_arc(e):
        return isinstance(e, (asin, acos))

    @staticmethod
    def __is_ez(e):
        return e.is_Pow and e.args[1] == 0 and len(e.args[0].free_symbols) > 0

    @staticmethod
    def __domain_sqrt(e, sym):
        return solveset(e.args[0] >= 0, sym, domain=S.Reals)

    @staticmethod
    def __domain_log(e, sym):
        return solveset(e.args[0] > 0, sym, domain=S.Reals)

    @staticmethod
    def __domain_invrat(e, sym):
        return S.Reals - solveset(e.args[0], sym, domain=S.Reals)

    @staticmethod
    def __domain_negpow(e, sym):
        fx, neg_mdivn = e.args
        m, n = fraction(-neg_mdivn)
        if is_odd(n):
            return S.Reals - solveset(fx, sym, domain=S.Reals)
        else:
            return solveset(fx > 0, sym, domain=S.Reals)

    @staticmethod
    def __domain_arc(e, sym):
        return Intersection(solveset(e.args[0] >= -1, sym, domain=S.Reals), solveset(e.args[0] <= 1, sym, domain=S.Reals))

    @staticmethod
    def __domain_ez(e, sym):
        return S.Reals - solveset(e.args[0], sym, domain=S.Reals)

    def __get_domain(self, e, d, sym):
        if sym not in e.free_symbols:
            pass
        elif self.__is_sqrt(e):
            d = Intersection(d, self.__domain_sqrt(e, sym))
        elif self.__is_log(e):
            d = Intersection(d, self.__domain_log(e, sym))
        # 特殊处理，分式 args 不会输出分子、分母, e.g. (1/(x - 2)**2) => (x - 2, -2)
        elif self.__is_negpow(e):
            d = Intersection(d, self.__domain_negpow(e, sym))
        elif self.__is_invrat(e):
            d = Intersection(d, self.__domain_invrat(e, sym))
        elif self.__is_arc(e):
            d = Intersection(d, self.__domain_arc(e, sym))
        elif self.__is_ez(e):
            d = Intersection(d, self.__domain_ez(e, sym))
        return d

    def __recur_domain(self, e, sym):
        if isinstance(e, (Symbol, Number)) or e == sympify('pi'):
            return
        if len(e.args) > 0:
            self.domain = self.__get_domain(e, self.domain, sym)
            for arg in e.args:
                self.__recur_domain(arg, sym)
            return


class HanShuDingYiYu(BaseFunction):
    """wrapper class of qiudingyiyu，输出 BaseSymbolValue"""

    def solver(self, *args):
        from mathsolver.functions.hanshu.helper import check_func
        func = check_func(args[0])
        dingyiyu_interval = QiuDingYiYu().solver(*args)
        self.steps.append(["", "函数 %s 的定义域为 %s" % (func.printing(), new_latex(dingyiyu_interval))])
        inter = BaseInter("R")
        inter.interval = dingyiyu_interval
        self.output.append(inter)
        # self.output.append(BaseSymbolValue({func.var: dingyiyu_Interval}))
        self.label.add("求函数定义域")
        return self


if __name__ == '__main__':
    pass
